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.
Added initial support for AIP1640 led driver, used in Wemos D1 mini's
matrix LED shield
- Loading branch information
1 parent
9a0f401
commit 8e63911
Showing
3 changed files
with
307 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,159 @@ | ||
package gpio | ||
|
||
import ( | ||
"time" | ||
|
||
"gobot.io/x/gobot" | ||
) | ||
|
||
const ( | ||
AIP1640DataCmd = 0x40 | ||
AIP1640DispCtrl = 0x88 | ||
AIP1640AddrCmd = 0xC0 | ||
|
||
AIP1640FixedAddr = 0x04 | ||
) | ||
|
||
// AIP1640Driver is the gobot driver for the AIP1640 LED driver used in the WEMOS D1 mini Matrix LED Shield. | ||
// It has some similarities with the TM16xx LED drivers | ||
// | ||
// Datasheet CN: https://datasheet.lcsc.com/szlcsc/AiP1640_C82650.pdf | ||
// | ||
// Library ported from: https://github.com/wemos/WEMOS_Matrix_LED_Shield_Arduino_Library | ||
type AIP1640Driver struct { | ||
pintClock *DirectPinDriver | ||
pinData *DirectPinDriver | ||
name string | ||
intensity byte | ||
buffer [8]byte | ||
connection gobot.Connection | ||
gobot.Commander | ||
} | ||
|
||
// NewAIP1640Driver return a new AIP1640Driver given a gobot.Connection and the clock, data and strobe pins | ||
func NewAIP1640Driver(a gobot.Connection, clockPin string, dataPin string) *AIP1640Driver { | ||
t := &AIP1640Driver{ | ||
name: gobot.DefaultName("AIP1640Driver"), | ||
pintClock: NewDirectPinDriver(a, clockPin), | ||
pinData: NewDirectPinDriver(a, dataPin), | ||
intensity: 7, | ||
connection: a, | ||
Commander: gobot.NewCommander(), | ||
} | ||
|
||
/* TODO : Add commands */ | ||
|
||
return t | ||
} | ||
|
||
// Start initializes the tm1638, it uses a SPI-like communication protocol | ||
func (a *AIP1640Driver) Start() (err error) { | ||
a.pinData.On() | ||
a.pintClock.On() | ||
|
||
return | ||
} | ||
|
||
// Halt implements the Driver interface | ||
func (a *AIP1640Driver) Halt() (err error) { return } | ||
|
||
// Name returns the AIP1640Drivers name | ||
func (a *AIP1640Driver) Name() string { return a.name } | ||
|
||
// SetName sets the AIP1640Drivers name | ||
func (a *AIP1640Driver) SetName(n string) { a.name = n } | ||
|
||
// Connection returns the AIP1640Driver Connection | ||
func (a *AIP1640Driver) Connection() gobot.Connection { | ||
return a.connection | ||
} | ||
|
||
// SetIntensity changes the intensity (from 1 to 7) of the display | ||
func (a *AIP1640Driver) SetIntensity(level byte) { | ||
if level >= 7 { | ||
level = 7 | ||
} | ||
a.intensity = level | ||
} | ||
|
||
// Display sends the buffer to the display (ie. turns on/off the corresponding LEDs) | ||
func (a *AIP1640Driver) Display() { | ||
for i := 0; i < 8; i++ { | ||
a.sendData(byte(i), a.buffer[i]) | ||
|
||
a.pinData.Off() | ||
a.pintClock.Off() | ||
time.Sleep(1 * time.Millisecond) | ||
a.pintClock.On() | ||
a.pinData.On() | ||
} | ||
|
||
a.sendCommand(AIP1640DispCtrl | a.intensity) | ||
} | ||
|
||
// Clear empties the buffer (turns off all the LEDs) | ||
func (a *AIP1640Driver) Clear() { | ||
for i := 0; i < 8; i++ { | ||
a.buffer[i] = 0x00 | ||
} | ||
} | ||
|
||
// DrawPixel turns on or off a specific in the buffer | ||
func (a *AIP1640Driver) DrawPixel(x, y byte, enabled bool) { | ||
if x >= 8 || y >= 8 { | ||
return | ||
} | ||
y = 7 - y | ||
if enabled { | ||
a.buffer[y] |= 1 << x | ||
} else { | ||
a.buffer[y] &^= 1 << x | ||
} | ||
} | ||
|
||
// DrawRow sets any given row of LEDs in the buffer | ||
func (a *AIP1640Driver) DrawRow(row, data byte) { | ||
if row >= 8 { | ||
return | ||
} | ||
a.buffer[7-row] = data | ||
} | ||
|
||
// DrawMatrix sets the whole buffer | ||
func (a *AIP1640Driver) DrawMatrix(data [8]byte) { | ||
for i := 0; i < 8; i++ { | ||
a.buffer[7-i] = data[i] | ||
} | ||
} | ||
|
||
// sendCommand is an auxiliary function to send commands to the AIP1640Driver module | ||
func (a *AIP1640Driver) sendCommand(cmd byte) { | ||
a.pinData.Off() | ||
a.send(cmd) | ||
a.pinData.On() | ||
} | ||
|
||
// sendData is an auxiliary function to send data to the AIP1640Driver module | ||
func (a *AIP1640Driver) sendData(address byte, data byte) { | ||
a.sendCommand(AIP1640DataCmd | AIP1640FixedAddr) | ||
a.pinData.Off() | ||
a.send(AIP1640AddrCmd | address) | ||
a.send(data) | ||
a.pinData.On() | ||
} | ||
|
||
// send writes data on the module | ||
func (a *AIP1640Driver) send(data byte) { | ||
for i := 0; i < 8; i++ { | ||
a.pintClock.Off() | ||
|
||
if (data & 1) > 0 { | ||
a.pinData.On() | ||
} else { | ||
a.pinData.Off() | ||
} | ||
data >>= 1 | ||
|
||
a.pintClock.On() | ||
} | ||
} |
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,93 @@ | ||
package gpio | ||
|
||
import ( | ||
"strings" | ||
"testing" | ||
|
||
"gobot.io/x/gobot" | ||
"gobot.io/x/gobot/gobottest" | ||
) | ||
|
||
var _ gobot.Driver = (*AIP1640Driver)(nil) | ||
|
||
// --------- HELPERS | ||
func initTestAIP1640Driver() (driver *AIP1640Driver) { | ||
driver, _ = initTestAIP1640DriverWithStubbedAdaptor() | ||
return | ||
} | ||
|
||
func initTestAIP1640DriverWithStubbedAdaptor() (*AIP1640Driver, *gpioTestAdaptor) { | ||
adaptor := newGpioTestAdaptor() | ||
return NewAIP1640Driver(adaptor, "1", "2"), adaptor | ||
} | ||
|
||
// --------- TESTS | ||
func TestAIP1640Driver(t *testing.T) { | ||
var a interface{} = initTestAIP1640Driver() | ||
_, ok := a.(*AIP1640Driver) | ||
if !ok { | ||
t.Errorf("NewAIP1640Driver() should have returned a *AIP1640Driver") | ||
} | ||
} | ||
|
||
func TestAIP1640DriverStart(t *testing.T) { | ||
d := initTestAIP1640Driver() | ||
gobottest.Assert(t, d.Start(), nil) | ||
} | ||
|
||
func TestAIP1640DriverHalt(t *testing.T) { | ||
d := initTestAIP1640Driver() | ||
gobottest.Assert(t, d.Halt(), nil) | ||
} | ||
|
||
func TestAIP1640DriverDefaultName(t *testing.T) { | ||
d := initTestAIP1640Driver() | ||
gobottest.Assert(t, strings.HasPrefix(d.Name(), "AIP1640Driver"), true) | ||
} | ||
|
||
func TestAIP1640DriverSetName(t *testing.T) { | ||
d := initTestAIP1640Driver() | ||
d.SetName("mybot") | ||
gobottest.Assert(t, d.Name(), "mybot") | ||
} | ||
|
||
func TestAIP1640DriveDrawPixel(t *testing.T) { | ||
d := initTestAIP1640Driver() | ||
d.DrawPixel(2, 3, true) | ||
d.DrawPixel(0, 3, true) | ||
gobottest.Assert(t, uint8(5), d.buffer[7-3]) | ||
} | ||
|
||
func TestAIP1640DriverDrawRow(t *testing.T) { | ||
d := initTestAIP1640Driver() | ||
d.DrawRow(4, 0x3C) | ||
gobottest.Assert(t, uint8(0x3C), d.buffer[7-4]) | ||
} | ||
|
||
func TestAIP1640DriverDrawMatrix(t *testing.T) { | ||
d := initTestAIP1640Driver() | ||
drawing := [8]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF} | ||
d.DrawMatrix(drawing) | ||
gobottest.Assert(t, [8]byte{0xEF, 0xCD, 0xAB, 0x89, 0x67, 0x45, 0x23, 0x01}, d.buffer) | ||
} | ||
|
||
func TestAIP1640DriverClear(t *testing.T) { | ||
d := initTestAIP1640Driver() | ||
drawing := [8]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF} | ||
d.DrawMatrix(drawing) | ||
gobottest.Assert(t, [8]byte{0xEF, 0xCD, 0xAB, 0x89, 0x67, 0x45, 0x23, 0x01}, d.buffer) | ||
d.Clear() | ||
gobottest.Assert(t, [8]byte{}, d.buffer) | ||
} | ||
|
||
func TestAIP1640DriverSetIntensity(t *testing.T) { | ||
d := initTestAIP1640Driver() | ||
d.SetIntensity(3) | ||
gobottest.Assert(t, uint8(3), d.intensity) | ||
} | ||
|
||
func TestAIP1640DriverSetIntensityHigherThan7(t *testing.T) { | ||
d := initTestAIP1640Driver() | ||
d.SetIntensity(19) | ||
gobottest.Assert(t, uint8(7), d.intensity) | ||
} |
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,55 @@ | ||
// +build example | ||
// | ||
// Do not build by default. | ||
|
||
/* | ||
How to run | ||
Pass serial port to use as the first param: | ||
go run examples/firmata_aip1640.go /dev/ttyACM0 | ||
*/ | ||
|
||
package main | ||
|
||
import ( | ||
"os" | ||
"time" | ||
|
||
"gobot.io/x/gobot" | ||
"gobot.io/x/gobot/drivers/gpio" | ||
"gobot.io/x/gobot/platforms/firmata" | ||
) | ||
|
||
func main() { | ||
|
||
firmataAdaptor := firmata.NewAdaptor(os.Args[1]) | ||
// In the WEMOS D1 Mini LED Matrix Shield clockPin = 14, dataPin = 13 | ||
aip1640 := gpio.NewAIP1640Driver(firmataAdaptor, "14", "13") | ||
|
||
smiles := [3][8]byte{ | ||
{0x3C, 0x42, 0xA5, 0x81, 0xA5, 0x99, 0x42, 0x3C}, // happy :) | ||
{0x3C, 0x42, 0xA5, 0x81, 0xBD, 0x81, 0x42, 0x3C}, // normal :| | ||
{0x3C, 0x42, 0xA5, 0x81, 0x99, 0xA5, 0x42, 0x3C}, // sad :( | ||
} | ||
|
||
s := 0 | ||
work := func() { | ||
aip1640.Clear() | ||
gobot.Every(600*time.Millisecond, func() { | ||
aip1640.DrawMatrix(smiles[s]) | ||
aip1640.Display() | ||
s++ | ||
if s > 2 { | ||
s = 0 | ||
} | ||
}) | ||
} | ||
|
||
robot := gobot.NewRobot("aip1640Bot", | ||
[]gobot.Connection{firmataAdaptor}, | ||
[]gobot.Device{aip1640}, | ||
work, | ||
) | ||
|
||
robot.Start() | ||
} |