sn3218 - add experimental support for sn3218 LED driver (#348)

pull/1/head
lnitram 8 years ago committed by M-A
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…
Cancel
Save