mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-07-26 10:38:41 +00:00
drivers: add driver for IS31FL3731 matrix LED driver (#370)
IS31FL3731: add driver for IS31FL3731 matrix LED driver
This commit is contained in:
@@ -157,6 +157,8 @@ smoke-test:
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/ws2812
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=feather-nrf52840 ./examples/is31fl3731/main.go
|
||||
@md5sum ./build/test.hex
|
||||
ifneq ($(AVR), 0)
|
||||
tinygo build -size short -o ./build/test.hex -target=arduino ./examples/ws2812
|
||||
@md5sum ./build/test.hex
|
||||
|
||||
@@ -87,6 +87,7 @@ The following 78 devices are supported.
|
||||
| [software I2C driver](https://www.ti.com/lit/an/slva704/slva704.pdf) | GPIO |
|
||||
| [ILI9341 TFT color display](https://cdn-shop.adafruit.com/datasheets/ILI9341.pdf) | SPI |
|
||||
| [INA260 Volt/Amp/Power meter](https://www.ti.com/lit/ds/symlink/ina260.pdf) | I2C |
|
||||
| [IS31FL3731 matrix LED driver](https://www.lumissil.com/assets/pdf/core/IS31FL3731_DS.pdf) | I2C |
|
||||
| [4x4 Membrane Keypad](https://cdn.sparkfun.com/assets/f/f/a/5/0/DS-16038.pdf) | GPIO |
|
||||
| [L293x motor driver](https://www.ti.com/lit/ds/symlink/l293d.pdf) | GPIO/PWM |
|
||||
| [L9110x motor driver](https://www.elecrow.com/download/datasheet-l9110.pdf) | GPIO/PWM |
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/is31fl3731"
|
||||
)
|
||||
|
||||
// I2CAddress -- address of led matrix
|
||||
var I2CAddress uint8 = is31fl3731.I2C_ADDRESS_74
|
||||
|
||||
func main() {
|
||||
bus := machine.I2C0
|
||||
err := bus.Configure(machine.I2CConfig{})
|
||||
if err != nil {
|
||||
println("could not configure I2C:", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Create driver for Adafruit 15x7 CharliePlex LED Matrix FeatherWing
|
||||
// (CharlieWing): https://www.adafruit.com/product/3163
|
||||
ledMatrix := is31fl3731.NewAdafruitCharlieWing15x7(bus, I2CAddress)
|
||||
|
||||
err = ledMatrix.Configure()
|
||||
if err != nil {
|
||||
println("could not configure is31fl3731 driver:", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Fill the whole matrix on the frame #0 (visible by default)
|
||||
ledMatrix.Fill(is31fl3731.FRAME_0, uint8(3))
|
||||
|
||||
// Draw couple pixels on the frame #1 (not visible yet)
|
||||
ledMatrix.DrawPixelXY(is31fl3731.FRAME_1, uint8(0), uint8(0), uint8(10))
|
||||
ledMatrix.DrawPixelXY(is31fl3731.FRAME_1, uint8(14), uint8(6), uint8(10))
|
||||
|
||||
// There are 8 frames available, it's a good idea to draw on an invisible
|
||||
// frame and then switch to that frame to reduce flickering. Switch between
|
||||
// frame #0 and #1 in a loop to show animation:
|
||||
for {
|
||||
println("show frame #0...")
|
||||
ledMatrix.SetActiveFrame(is31fl3731.FRAME_0)
|
||||
time.Sleep(time.Second * 3)
|
||||
|
||||
println("show frame #1...")
|
||||
ledMatrix.SetActiveFrame(is31fl3731.FRAME_1)
|
||||
time.Sleep(time.Second * 3)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
// Package is31fl3731 provides a driver for the Lumissil IS31FL3731 matrix LED
|
||||
// driver.
|
||||
//
|
||||
// Driver supports following layouts:
|
||||
// - any custom LED matrix layout
|
||||
// - Adafruit 15x7 CharliePlex LED Matrix FeatherWing (CharlieWing)
|
||||
// https://www.adafruit.com/product/3163
|
||||
//
|
||||
// Datasheet:
|
||||
// https://www.lumissil.com/assets/pdf/core/IS31FL3731_DS.pdf
|
||||
//
|
||||
// This driver inspired by Adafruit Python driver:
|
||||
// https://github.com/adafruit/Adafruit_CircuitPython_IS31FL3731
|
||||
//
|
||||
package is31fl3731
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
// Device implements TinyGo driver for Lumissil IS31FL3731 matrix LED driver
|
||||
type Device struct {
|
||||
Address uint8
|
||||
bus drivers.I2C
|
||||
|
||||
// Currently selected command register (one of the frame registers or the
|
||||
// function register)
|
||||
selectedCommand uint8
|
||||
}
|
||||
|
||||
// Configure chip for operating as a LED matrix display
|
||||
func (d *Device) Configure() (err error) {
|
||||
// Shutdown software
|
||||
err = d.writeFunctionRegister(SET_SHUTDOWN, []byte{SOFTWARE_OFF})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to shutdown: %w", err)
|
||||
}
|
||||
|
||||
time.Sleep(time.Millisecond * 10)
|
||||
|
||||
// Wake up software
|
||||
err = d.writeFunctionRegister(SET_SHUTDOWN, []byte{SOFTWARE_ON})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to wake up: %w", err)
|
||||
}
|
||||
|
||||
// Set display to a picture mode ("auto frame play mode" and "audio frame play
|
||||
// mode" are not supported in this version of the driver)
|
||||
err = d.writeFunctionRegister(SET_DISPLAY_MODE, []byte{DISPLAY_MODE_PICTURE})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to switch to a picture move: %w", err)
|
||||
}
|
||||
|
||||
// Enable LEDs that are present (soldered) on the board. From the datasheet:
|
||||
// LEDs which are no connected must be off by LED Control Register (Frame
|
||||
// Registers) or it will affect other LEDs
|
||||
err = d.enableLEDs()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to enable LEDs: %w", err)
|
||||
}
|
||||
|
||||
// Disable audiosync
|
||||
err = d.writeFunctionRegister(SET_AUDIOSYNC, []byte{AUDIOSYNC_OFF})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to disable audiosync: %w", err)
|
||||
}
|
||||
|
||||
// Clear all frames
|
||||
for frame := FRAME_0; frame <= FRAME_7; frame++ {
|
||||
err = d.Clear(frame)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to clear frame %d: %w", frame, err)
|
||||
}
|
||||
}
|
||||
|
||||
// 1st frame is displayed by default
|
||||
err = d.SetActiveFrame(FRAME_0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to set active frame: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// selectCommand selects command register, can be:
|
||||
// - frame registers 0-7
|
||||
// - function register
|
||||
func (d *Device) selectCommand(command uint8) (err error) {
|
||||
if command != d.selectedCommand {
|
||||
d.selectedCommand = command
|
||||
return d.bus.WriteRegister(d.Address, COMMAND, []byte{command})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeFunctionRegister selects the function register and writes data into it
|
||||
func (d *Device) writeFunctionRegister(operation uint8, data []byte) (err error) {
|
||||
err = d.selectCommand(FUNCTION)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return d.bus.WriteRegister(d.Address, operation, data)
|
||||
}
|
||||
|
||||
// enableLEDs enables only LEDs that are soldered on the set board. Enabled
|
||||
// all 16x9 LEDs by default
|
||||
func (d *Device) enableLEDs() (err error) {
|
||||
for frame := FRAME_0; frame <= FRAME_7; frame++ {
|
||||
err = d.selectCommand(frame)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Enable every LED (16 columns x 9 rows)
|
||||
for i := uint8(0); i < 16; i++ {
|
||||
err = d.bus.WriteRegister(d.Address, i, []byte{0xFF})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// setPixelPWD sets individual pixel's PWM value [0-255] on the selected frame
|
||||
func (d *Device) setPixelPWD(frame, n, value uint8) (err error) {
|
||||
err = d.selectCommand(frame)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return d.bus.WriteRegister(d.Address, LED_PWM_OFFSET+n, []byte{value})
|
||||
}
|
||||
|
||||
// SetActiveFrame sets frame to display with LEDs
|
||||
func (d *Device) SetActiveFrame(frame uint8) (err error) {
|
||||
if frame > FRAME_7 {
|
||||
return fmt.Errorf("frame %d is out of valid range [0-7]", frame)
|
||||
}
|
||||
|
||||
return d.writeFunctionRegister(SET_ACTIVE_FRAME, []byte{frame})
|
||||
}
|
||||
|
||||
// Fill the whole frame with provided PWM value [0-255]
|
||||
func (d *Device) Fill(frame, value uint8) (err error) {
|
||||
if frame > FRAME_7 {
|
||||
return fmt.Errorf("frame %d is out of valid range [0-7]", frame)
|
||||
}
|
||||
|
||||
err = d.selectCommand(frame)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data := make([]byte, 24)
|
||||
for i := range data {
|
||||
data[i] = value
|
||||
}
|
||||
|
||||
for i := uint8(0); i < 6; i++ {
|
||||
err = d.bus.WriteRegister(d.Address, LED_PWM_OFFSET+i*24, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Clear the whole frame
|
||||
func (d *Device) Clear(frame uint8) (err error) {
|
||||
return d.Fill(frame, 0x00)
|
||||
}
|
||||
|
||||
// DrawPixelIndex draws a single pixel on the selected frame by its index with
|
||||
// provided PWM value [0-255]
|
||||
func (d *Device) DrawPixelIndex(frame, index, value uint8) (err error) {
|
||||
if frame > FRAME_7 {
|
||||
return fmt.Errorf("frame %d is out of valid range [0-7]", frame)
|
||||
}
|
||||
|
||||
return d.setPixelPWD(frame, index, value)
|
||||
}
|
||||
|
||||
// DrawPixelXY draws a single pixel on the selected frame by its XY coordinates
|
||||
// with provided PWM value [0-255]. Raw LEDs layout assumed to be a 16x9 matrix,
|
||||
// and can be used with any custom board that has IS31FL3731 driver.
|
||||
func (d *Device) DrawPixelXY(frame, x, y, value uint8) (err error) {
|
||||
return d.setPixelPWD(frame, 16*x+y, value)
|
||||
}
|
||||
|
||||
// New creates a raw driver w/o any preset board layout.
|
||||
// Addresses:
|
||||
// - 0x74 (AD pin connected to GND)
|
||||
// - 0x75 (AD pin connected to SCL)
|
||||
// - 0x76 (AD pin connected to SDA)
|
||||
// - 0x77 (AD pin connected to VCC)
|
||||
func New(bus drivers.I2C, address uint8) Device {
|
||||
return Device{
|
||||
Address: address,
|
||||
bus: bus,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package is31fl3731
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
// DeviceAdafruitCharlieWing15x7 implements TinyGo driver for Lumissil
|
||||
// IS31FL3731 matrix LED driver on Adafruit 15x7 CharliePlex LED Matrix
|
||||
// FeatherWing (CharlieWing) board: https://www.adafruit.com/product/3163
|
||||
type DeviceAdafruitCharlieWing15x7 struct {
|
||||
Device
|
||||
}
|
||||
|
||||
// enableLEDs enables only LEDs that are soldered on the Adafruit CharlieWing
|
||||
// board. The board has following LEDs matrix layout:
|
||||
//
|
||||
// "o" - connected (soldered) LEDs
|
||||
// "x" - not connected LEDs
|
||||
//
|
||||
// + - - - - - - - - - - - - - - +
|
||||
// | + - - - - - - - - - - - - + |
|
||||
// | | | |
|
||||
// | | v v
|
||||
// +---------------------------------+
|
||||
// | o o o o o o o o o o o o o o o x |
|
||||
// | o o o o o o o o o o o o o o o x |
|
||||
// | o o o o o o o o o o o o o o o x |
|
||||
// | o o o o o o o o o o o o o o o x |
|
||||
// | o o o o o o o o o o o o o o o x |
|
||||
// | o o o o o o o o o o o o o o o x |
|
||||
// | o o o o o o o o o o o o o o o x |
|
||||
// | x x x x x x x x x x x x x x x x |
|
||||
// +---------------------------------+
|
||||
// ^ ^ | |
|
||||
// | | ... - - + |
|
||||
// | + - - - - - - - - - - - - - +
|
||||
// |
|
||||
// start (address 0x00)
|
||||
//
|
||||
func (d *DeviceAdafruitCharlieWing15x7) enableLEDs() (err error) {
|
||||
for frame := FRAME_0; frame <= FRAME_7; frame++ {
|
||||
err = d.selectCommand(frame)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Enable left half
|
||||
for i := uint8(0); i < 16; i += 2 {
|
||||
err = d.bus.WriteRegister(d.Address, i, []byte{0b11111110})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Enable right half
|
||||
for i := uint8(3); i < 16; i += 2 {
|
||||
err = d.bus.WriteRegister(d.Address, i, []byte{0b01111111})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Disable invisible column on the right side
|
||||
err = d.bus.WriteRegister(d.Address, 1, []byte{0b00000000})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DrawPixelXY draws a single pixel on the selected frame by its XY coordinates
|
||||
// with provided PWM value [0-255]
|
||||
func (d *DeviceAdafruitCharlieWing15x7) DrawPixelXY(frame, x, y, value uint8) (err error) {
|
||||
var index uint8
|
||||
|
||||
if x >= 15 {
|
||||
return fmt.Errorf("invalid value: X is out of range [0, 15]")
|
||||
} else if y >= 7 {
|
||||
return fmt.Errorf("invalid value: Y is out of range [0, 7]")
|
||||
}
|
||||
|
||||
// Board is one pixel shorter (7 vs 8 supported pixels)
|
||||
if x < 8 {
|
||||
index = 16*x + y + 1
|
||||
} else {
|
||||
index = 16*(16-x) - y - 1 - 1
|
||||
}
|
||||
|
||||
return d.setPixelPWD(frame, index, value)
|
||||
}
|
||||
|
||||
// NewAdafruitCharlieWing15x7 creates a new driver with Adafruit 15x7
|
||||
// CharliePlex LED Matrix FeatherWing (CharlieWing) layout.
|
||||
// Available addresses:
|
||||
// - 0x74 (default)
|
||||
// - 0x77 (when the address jumper soldered)
|
||||
func NewAdafruitCharlieWing15x7(bus drivers.I2C, address uint8) DeviceAdafruitCharlieWing15x7 {
|
||||
return DeviceAdafruitCharlieWing15x7{
|
||||
Device: Device{
|
||||
Address: address,
|
||||
bus: bus,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package is31fl3731
|
||||
|
||||
// Registers. Names taken from the datasheet:
|
||||
// https://www.lumissil.com/assets/pdf/core/IS31FL3731_DS.pdf
|
||||
const (
|
||||
// AD pin connected to GND
|
||||
I2C_ADDRESS_74 uint8 = 0x74
|
||||
// AD pin connected to SCL
|
||||
I2C_ADDRESS_75 uint8 = 0x75
|
||||
// AD pin connected to SDA
|
||||
I2C_ADDRESS_76 uint8 = 0x76
|
||||
// AD pin connected to VCC
|
||||
I2C_ADDRESS_77 uint8 = 0x77
|
||||
|
||||
// Main command register
|
||||
COMMAND uint8 = 0xFD
|
||||
|
||||
// Commands for each of 8 frames
|
||||
FRAME_0 uint8 = 0x00
|
||||
FRAME_1 uint8 = 0x01
|
||||
FRAME_2 uint8 = 0x02
|
||||
FRAME_3 uint8 = 0x03
|
||||
FRAME_4 uint8 = 0x04
|
||||
FRAME_5 uint8 = 0x05
|
||||
FRAME_6 uint8 = 0x06
|
||||
FRAME_7 uint8 = 0x07
|
||||
|
||||
// Command to set configuration
|
||||
FUNCTION uint8 = 0x0B
|
||||
|
||||
// Configuration:
|
||||
SET_DISPLAY_MODE uint8 = 0x00
|
||||
SET_ACTIVE_FRAME uint8 = 0x01
|
||||
SET_AUDIOSYNC uint8 = 0x06
|
||||
SET_SHUTDOWN uint8 = 0x0A
|
||||
|
||||
// Configuration: display mode
|
||||
DISPLAY_MODE_PICTURE uint8 = 0x00
|
||||
|
||||
// Configuration: audiosync (enable audio signal to modulate the intensity of
|
||||
// the matrix)
|
||||
AUDIOSYNC_OFF uint8 = 0x00
|
||||
AUDIOSYNC_ON uint8 = 0x01
|
||||
|
||||
// Configuration: software shutdown
|
||||
SOFTWARE_OFF uint8 = 0x00
|
||||
SOFTWARE_ON uint8 = 0x01
|
||||
|
||||
// Frame LEDs
|
||||
LED_CONTROL_OFFSET uint8 = 0x00 // to on/off each LED
|
||||
LED_PWM_OFFSET uint8 = 0x24 // to set PWM (0-255) for each LED
|
||||
)
|
||||
Reference in New Issue
Block a user