mirror of https://github.com/periph/devices
sn3218 - add experimental support for sn3218 LED driver (#348)
parent
34b5f2bfcb
commit
e17c8b885b
@ -0,0 +1,10 @@
|
|||||||
|
// Copyright 2018 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 sn3218 controls a SN3218 LED driver with 18 LEDs over an i2c bus.
|
||||||
|
//
|
||||||
|
// Datasheet
|
||||||
|
//
|
||||||
|
// http://www.si-en.com/uploadpdf/s2011517171720.pdf
|
||||||
|
package sn3218
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
// Copyright 2018 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 sn3218_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"periph.io/x/periph/conn/i2c/i2creg"
|
||||||
|
"periph.io/x/periph/experimental/devices/sn3218"
|
||||||
|
"periph.io/x/periph/host"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Example() {
|
||||||
|
if _, err := host.Init(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := i2creg.Open("")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer b.Close()
|
||||||
|
|
||||||
|
d, err := sn3218.New(b)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer d.Halt()
|
||||||
|
|
||||||
|
// By default, the device is disabled and brightness is 0 for all LEDs
|
||||||
|
// So let's set the brightness to a low value and enable the device to
|
||||||
|
// get started
|
||||||
|
d.BrightnessAll(1)
|
||||||
|
d.Sleep()
|
||||||
|
|
||||||
|
// Switch LED 7 on
|
||||||
|
if err := d.Switch(7, true); err != nil {
|
||||||
|
log.Fatal("Error while switching LED", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(1000 * time.Millisecond)
|
||||||
|
}
|
||||||
@ -0,0 +1,141 @@
|
|||||||
|
// Copyright 2018 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 sn3218
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"periph.io/x/periph/conn/i2c"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
i2cAddress = 0x54
|
||||||
|
cmdEnableOutput = 0x00
|
||||||
|
cmdSetBrightnessValues = 0x01
|
||||||
|
cmdEnableLeds = 0x13
|
||||||
|
cmdUpdate = 0x16
|
||||||
|
cmdReset = 0x17
|
||||||
|
)
|
||||||
|
|
||||||
|
// Dev is a handler to sn3218 controller.
|
||||||
|
type Dev struct {
|
||||||
|
i2c i2c.Dev
|
||||||
|
on [18]bool
|
||||||
|
brightness [18]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a handle to a SN3218 LED driver.
|
||||||
|
func New(bus i2c.Bus) (*Dev, error) {
|
||||||
|
d := &Dev{
|
||||||
|
i2c: i2c.Dev{Bus: bus, Addr: i2cAddress},
|
||||||
|
}
|
||||||
|
if err := d.reset(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return d, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Halt resets the registers and switches the driver off.
|
||||||
|
func (d *Dev) Halt() error {
|
||||||
|
return d.reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
// WakeUp returns from sleep mode and switches the channels according to the states in the register of SN3218.
|
||||||
|
func (d *Dev) WakeUp() error {
|
||||||
|
_, err := d.i2c.Write([]byte{cmdEnableOutput, 0x01})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sleep sends SN3218 to sleep mode while keeping the states in the registers.
|
||||||
|
func (d *Dev) Sleep() error {
|
||||||
|
_, err := d.i2c.Write([]byte{cmdEnableOutput, 0x00})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetState returns the state (on/off) and the brightness (0..255) of the
|
||||||
|
// Channel 0..17.
|
||||||
|
func (d *Dev) GetState(channel int) (bool, byte, error) {
|
||||||
|
if channel < 0 || channel >= 18 {
|
||||||
|
return false, 0, errors.New("channel number out of range 0..17")
|
||||||
|
}
|
||||||
|
return d.on[channel], d.brightness[channel], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Switch switched the channel (0..18) to state (on/off).
|
||||||
|
func (d *Dev) Switch(channel int, state bool) error {
|
||||||
|
if channel < 0 || channel >= 18 {
|
||||||
|
return errors.New("channel number out of range 0..17")
|
||||||
|
}
|
||||||
|
d.on[channel] = state
|
||||||
|
return d.updateStates()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SwitchAll switches all channels accoring to the state (on/off).
|
||||||
|
func (d *Dev) SwitchAll(state bool) error {
|
||||||
|
for i := 0; i < 18; i++ {
|
||||||
|
d.on[i] = state
|
||||||
|
}
|
||||||
|
return d.updateStates()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Brightness sets the brightness of led (0..17) to value (0..255).
|
||||||
|
func (d *Dev) Brightness(channel int, value byte) error {
|
||||||
|
if channel < 0 || channel >= 18 {
|
||||||
|
return errors.New("channel number out of range 0..17")
|
||||||
|
}
|
||||||
|
d.brightness[channel] = value
|
||||||
|
return d.updateBrightness()
|
||||||
|
}
|
||||||
|
|
||||||
|
// BrightnessAll sets the brightness of all channels to the value (0..255).
|
||||||
|
func (d *Dev) BrightnessAll(value byte) error {
|
||||||
|
for i := 0; i < 18; i++ {
|
||||||
|
d.brightness[i] = value
|
||||||
|
}
|
||||||
|
return d.updateBrightness()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset resets the registers to the default values.
|
||||||
|
func (d *Dev) reset() error {
|
||||||
|
_, err := d.i2c.Write([]byte{cmdReset, 0xFF})
|
||||||
|
d.on = [18]bool{}
|
||||||
|
d.brightness = [18]byte{}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dev) stateArrayToInt() uint {
|
||||||
|
var result uint = 0
|
||||||
|
for i := uint(0); i < uint(18); i++ {
|
||||||
|
state := uint(1)
|
||||||
|
if !d.on[i] {
|
||||||
|
state = uint(0)
|
||||||
|
}
|
||||||
|
result |= (state << i)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dev) update() error {
|
||||||
|
_, err := d.i2c.Write([]byte{cmdUpdate, 0xFF})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dev) updateStates() error {
|
||||||
|
mask := d.stateArrayToInt()
|
||||||
|
cmd := [...]byte{cmdEnableLeds, byte(mask & 0x3F), byte((mask >> 6) & 0x3F), byte((mask >> 12) & 0X3F)}
|
||||||
|
if _, err := d.i2c.Write(cmd[:]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return d.update()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dev) updateBrightness() error {
|
||||||
|
cmd := [19]byte{cmdSetBrightnessValues}
|
||||||
|
copy(cmd[1:], d.brightness[:])
|
||||||
|
if _, err := d.i2c.Write(cmd[:]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return d.update()
|
||||||
|
}
|
||||||
@ -0,0 +1,230 @@
|
|||||||
|
// Copyright 2018 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 sn3218
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"periph.io/x/periph/conn/i2c/i2ctest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setup() *i2ctest.Record {
|
||||||
|
return &i2ctest.Record{
|
||||||
|
Bus: nil,
|
||||||
|
Ops: []i2ctest.IO{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNew(t *testing.T) {
|
||||||
|
bus := setup()
|
||||||
|
_, err := New(bus)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("New should not return error", err)
|
||||||
|
}
|
||||||
|
if len(bus.Ops) > 1 {
|
||||||
|
t.Fatal("Expected 0 operation to I2CBus, got ", len(bus.Ops))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(bus.Ops[0].W, []byte{0x17, 0xFF}) {
|
||||||
|
t.Fatal("Expected: 0x17, 0xFF (reset), got: ", bus.Ops[0].W)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHalt(t *testing.T) {
|
||||||
|
bus := setup()
|
||||||
|
dev, _ := New(bus)
|
||||||
|
err := dev.Halt()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Halt should not return error, but did", err)
|
||||||
|
}
|
||||||
|
if len(bus.Ops) != 2 {
|
||||||
|
t.Fatal("Expected 2 operations, got", len(bus.Ops))
|
||||||
|
}
|
||||||
|
if !bytes.Equal(bus.Ops[1].W, []byte{0x17, 0xFF}) {
|
||||||
|
t.Fatal("I2C write different than expected (reset)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWakeUp(t *testing.T) {
|
||||||
|
bus := setup()
|
||||||
|
dev, _ := New(bus)
|
||||||
|
dev.WakeUp()
|
||||||
|
if len(bus.Ops) != 2 {
|
||||||
|
t.Fatal("Expected 2 operations, got", len(bus.Ops))
|
||||||
|
}
|
||||||
|
if bus.Ops[1].Addr != 0x54 {
|
||||||
|
t.Fatal("Expected: Write to address 0x54, got: ", bus.Ops[1].Addr)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(bus.Ops[1].W, []byte{0x00, 0x01}) {
|
||||||
|
t.Fatal("Expected: 0x00, 0x01, got: ", bus.Ops[1].W)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSleep(t *testing.T) {
|
||||||
|
bus := setup()
|
||||||
|
dev, _ := New(bus)
|
||||||
|
dev.Sleep()
|
||||||
|
if !bytes.Equal(bus.Ops[1].W, []byte{0x00, 0x00}) {
|
||||||
|
t.Fatal("Expected: 0x00, 0x00, got: ", bus.Ops[1].W)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetState(t *testing.T) {
|
||||||
|
bus := setup()
|
||||||
|
dev, _ := New(bus)
|
||||||
|
state, brightness, err := dev.GetState(0)
|
||||||
|
if state != false || brightness != 0 || err != nil {
|
||||||
|
t.Fatal("Expected: false, 0, nil, got: ", state, brightness, err)
|
||||||
|
}
|
||||||
|
if _, _, err := dev.GetState(-1); err == nil {
|
||||||
|
t.Fatal("Expected error, but error is nil")
|
||||||
|
}
|
||||||
|
if _, _, err := dev.GetState(18); err == nil {
|
||||||
|
t.Fatal("Expected error, but error is nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSwitch(t *testing.T) {
|
||||||
|
bus := setup()
|
||||||
|
dev, _ := New(bus)
|
||||||
|
err := dev.Switch(7, true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Expected: err == nil, got:", err)
|
||||||
|
}
|
||||||
|
if state, _, _ := dev.GetState(7); !state {
|
||||||
|
t.Fatal("Expected: LED on, but was off")
|
||||||
|
}
|
||||||
|
dev.Switch(7, false)
|
||||||
|
if state, _, _ := dev.GetState(7); state {
|
||||||
|
t.Fatal("Expected: LED off, but was on")
|
||||||
|
}
|
||||||
|
if len(bus.Ops) != 5 {
|
||||||
|
t.Fatal("Expected 5 i2c writes, got: ", len(bus.Ops))
|
||||||
|
}
|
||||||
|
if !bytes.Equal(bus.Ops[1].W, []byte{0x13, 0x00, 0x02, 0x00}) {
|
||||||
|
t.Fatal("Expected 0x13, 0x00, 0x02, 0x00, got:", bus.Ops[1].W)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(bus.Ops[2].W, []byte{0x16, 0xFF}) {
|
||||||
|
t.Fatal("Expected 0x16, 0xFF got:", bus.Ops[2].W)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(bus.Ops[3].W, []byte{0x13, 0x00, 0x00, 0x00}) {
|
||||||
|
t.Fatal("Expected 0x13, 0x00, 0x00, 0x00, got: ", bus.Ops[3].W)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(bus.Ops[4].W, []byte{0x16, 0xFF}) {
|
||||||
|
t.Fatal("Expected 0x16, 0xFF got:", bus.Ops[4].W)
|
||||||
|
}
|
||||||
|
if err = dev.Switch(19, true); err == nil {
|
||||||
|
t.Fatal("Tried to switch LED out of range and expected error, but error is nil...")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetGlobalBrightness(t *testing.T) {
|
||||||
|
bus := setup()
|
||||||
|
dev, _ := New(bus)
|
||||||
|
dev.BrightnessAll(100)
|
||||||
|
for i := 0; i < 17; i++ {
|
||||||
|
if dev.brightness[i] != 100 {
|
||||||
|
t.Fatal("Brightness of LED", i, " should be 100, but is", dev.brightness[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(bus.Ops) != 3 {
|
||||||
|
t.Fatal("Expected 3 operations on I2C, got", len(bus.Ops))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(bus.Ops[1].W, []byte{0x01, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64}) {
|
||||||
|
t.Fatal("Write operation to I2C different than expected")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(bus.Ops[2].W, []byte{0x16, 0xFF}) {
|
||||||
|
t.Fatal("Expected update command, but got something else")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetBrightness(t *testing.T) {
|
||||||
|
bus := setup()
|
||||||
|
dev, _ := New(bus)
|
||||||
|
if _, brightness, _ := dev.GetState(9); brightness != 0 {
|
||||||
|
t.Fatal("Brightness should be 0, but it's not")
|
||||||
|
}
|
||||||
|
if err := dev.Brightness(9, 8); err != nil {
|
||||||
|
t.Fatal("There should be no error, but it is", err)
|
||||||
|
}
|
||||||
|
if _, brightness, _ := dev.GetState(9); brightness != 8 {
|
||||||
|
t.Fatal("Brightness should be 8, but it's not")
|
||||||
|
}
|
||||||
|
if len(bus.Ops) != 3 {
|
||||||
|
t.Fatal("Expected 3 i2c operations, got", len(bus.Ops))
|
||||||
|
}
|
||||||
|
if !bytes.Equal(bus.Ops[1].W, []byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0}) {
|
||||||
|
t.Fatal("Write operation to I2C different than expected")
|
||||||
|
}
|
||||||
|
if err := dev.Brightness(42, 100); err == nil {
|
||||||
|
t.Fatal("Expected error because channel out of range, but error was nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSwitchAll(t *testing.T) {
|
||||||
|
bus := setup()
|
||||||
|
dev, _ := New(bus)
|
||||||
|
dev.SwitchAll(true)
|
||||||
|
for i := 0; i < 17; i++ {
|
||||||
|
if state, _, _ := dev.GetState(i); !state {
|
||||||
|
t.Fatal("LED should be on, but is off: ", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(bus.Ops) != 3 {
|
||||||
|
t.Fatal("Expected 3 operations on I2C, got", len(bus.Ops))
|
||||||
|
}
|
||||||
|
if !bytes.Equal(bus.Ops[1].W, []byte{19, 63, 63, 63}) {
|
||||||
|
t.Fatal("Data written to bus different than expected")
|
||||||
|
}
|
||||||
|
|
||||||
|
dev.SwitchAll(false)
|
||||||
|
for i := 0; i < 17; i++ {
|
||||||
|
if state, _, _ := dev.GetState(i); state {
|
||||||
|
t.Fatal("LED should be off, but is on: ", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(bus.Ops) != 5 {
|
||||||
|
t.Fatal("Expected 4 operations on I2C, got", len(bus.Ops))
|
||||||
|
}
|
||||||
|
if !bytes.Equal(bus.Ops[3].W, []byte{19, 0, 0, 0}) {
|
||||||
|
t.Fatal("Data written to bus different than expected")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBoolArrayToInt(t *testing.T) {
|
||||||
|
bus := setup()
|
||||||
|
dev, _ := New(bus)
|
||||||
|
|
||||||
|
result := dev.stateArrayToInt()
|
||||||
|
if result != 0 {
|
||||||
|
t.Error("Expected: 0, got: ", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
dev.on[0] = true
|
||||||
|
result = dev.stateArrayToInt()
|
||||||
|
if result != 1 {
|
||||||
|
t.Error("Expected: 1, got: ", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
dev.on[1] = true
|
||||||
|
result = dev.stateArrayToInt()
|
||||||
|
if result != 3 {
|
||||||
|
t.Error("Expected: 3, got ", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 18; i++ {
|
||||||
|
dev.on[i] = true
|
||||||
|
}
|
||||||
|
result = dev.stateArrayToInt()
|
||||||
|
if result != 262143 {
|
||||||
|
t.Error("Expected: 262143, got ", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue