Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions examples/unoqmatrix/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package main

import (
"machine"

"image/color"
"math/rand"

"tinygo.org/x/drivers/unoqmatrix"
)

var on = color.RGBA{255, 255, 255, 255}

func main() {
display := unoqmatrix.New(machine.PF0)
display.ClearDisplay()

w, h := display.Size()
x := int16(0)
y := int16(0)
deltaX := int16(1)
deltaY := int16(1)

for {
pixel := display.GetPixel(x, y)
if pixel.R != 0 || pixel.G != 0 || pixel.B != 0 {
display.ClearDisplay()
x = 1 + int16(rand.Int31n(3))
y = 1 + int16(rand.Int31n(3))
deltaX = 1
deltaY = 1
if rand.Int31n(2) == 0 {
deltaX = -1
}
if rand.Int31n(2) == 0 {
deltaY = -1
}
}
display.SetPixel(x, y, on)

x += deltaX
y += deltaY

if x == 0 || x == w-1 {
deltaX = -deltaX
}

if y == 0 || y == h-1 {
deltaY = -deltaY
}

display.Display()
}
}
249 changes: 249 additions & 0 deletions unoqmatrix/matrix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
// Package unoqmatrix provides a driver for the UnoQMatrix LED matrix display.
//
// The UnoQMatrix is an 8x13 LED matrix display that can be controlled using a single pin.
// It uses a multiplexing technique to control the LEDs, which allows for a large number of LEDs to be controlled with fewer pins.
//
// This driver provides basic functionality to set individual pixels, clear the display, and refresh the display.
//
// Note: The UnoQMatrix does not support brightness control or color depth. Each pixel can only be turned on or off.
// Could it suppport brightness control by using PWM on the pin? To be investigated.
package unoqmatrix

import (
"machine"

"image/color"
"time"
)

type Config struct {
// Rotation of the LED matrix.
Rotation uint8
}

// Valid values:
//
// 0: regular orientation, (0 degree rotation)
// 1: 90 degree rotation clockwise
// 2: 180 degree rotation clockwise
// 3: 270 degree rotation clockwise
const (
RotationNormal = 0
Rotation90 = 1
Rotation180 = 2
Rotation270 = 3
)

const (
ledRows = 8
ledCols = 13

pixelRefreshDelay = 10 * time.Microsecond
)

// Device represents the UnoQMatrix LED matrix display.
type Device struct {
pin machine.Pin
buffer [ledRows][ledCols]color.RGBA
rotation uint8
}

// New returns a new unoqmatrix driver.
// The provided pin is the base pin number for the display. The driver will use this
// pin and the next 10 pins to control the LEDs.
func New(pin machine.Pin) Device {
return Device{pin: pin}
}

// Configure sets up the device.
func (d *Device) Configure(cfg Config) {
// TODO: implement rotation support
//d.SetRotation(cfg.Rotation)
}

// SetRotation changes the rotation of the LED matrix.
//
// Valid values for rotation:
//
// 0: regular orientation, (0 degree rotation)
// 1: 90 degree rotation clockwise
// 2: 180 degree rotation clockwise
// 3: 270 degree rotation clockwise
func (d *Device) SetRotation(rotation uint8) {
d.rotation = rotation % 4
}

// SetPixel sets the color of a specific pixel.
func (d *Device) SetPixel(x int16, y int16, c color.RGBA) {
d.buffer[y][x] = c
}

// GetPixel returns the color of a specific pixel.
func (d *Device) GetPixel(x int16, y int16) color.RGBA {
return d.buffer[y][x]
}

// Display sends the buffer (if any) to the screen. Takes 103 * pixelRefreshDelay to complete.
func (d *Device) Display() error {
for row := 0; row < ledRows; row++ {
for col := 0; col < ledCols; col++ {
d.clearDisplay()

c := d.buffer[row][col]
if c.R != 0 || c.G != 0 || c.B != 0 {
d.turnLedOn(row*ledCols + col)
}

// Sleep for a very short duration to allow the LED to light up before moving to the next one.
time.Sleep(pixelRefreshDelay)
}
}

return nil
}

// ClearDisplay turns off all the LEDs on the display.
func (d *Device) ClearDisplay() {
for row := 0; row < ledRows; row++ {
for col := 0; col < ledCols; col++ {
d.buffer[row][col] = color.RGBA{0, 0, 0, 255}
}
}
}

// Size returns the current size of the display.
func (d *Device) Size() (w, h int16) {
return ledCols, ledRows
}

// pinMapping defines the mapping of LED indices to pin pairs. Each entry corresponds
// to an LED index (0-104) and contains the two pin numbers that need to be set to turn on that LED.
// based on https://github.com/arduino/ArduinoCore-zephyr/blob/main/loader/matrix.inc#L13
var pinMapping = [][2]machine.Pin{
{0, 1}, // 0
{1, 0},
{0, 2},
{2, 0},
{1, 2},
{2, 1},
{0, 3},
{3, 0},
{1, 3},
{3, 1},
{2, 3}, // 10
{3, 2},
{0, 4},
{4, 0},
{1, 4},
{4, 1},
{2, 4},
{4, 2},
{3, 4},
{4, 3},
{0, 5}, // 20
{5, 0},
{1, 5},
{5, 1},
{2, 5},
{5, 2},
{3, 5},
{5, 3},
{4, 5},
{5, 4},
{0, 6}, // 30
{6, 0},
{1, 6},
{6, 1},
{2, 6},
{6, 2},
{3, 6},
{6, 3},
{4, 6},
{6, 4},
{5, 6}, // 40
{6, 5},
{0, 7},
{7, 0},
{1, 7},
{7, 1},
{2, 7},
{7, 2},
{3, 7},
{7, 3},
{4, 7}, // 50
{7, 4},
{5, 7},
{7, 5},
{6, 7},
{7, 6},
{0, 8},
{8, 0},
{1, 8},
{8, 1},
{2, 8}, // 60
{8, 2},
{3, 8},
{8, 3},
{4, 8},
{8, 4},
{5, 8},
{8, 5},
{6, 8},
{8, 6},
{7, 8}, // 70
{8, 7},
{0, 9},
{9, 0},
{1, 9},
{9, 1},
{2, 9},
{9, 2},
{3, 9},
{9, 3},
{4, 9}, // 80
{9, 4},
{5, 9},
{9, 5},
{6, 9},
{9, 6},
{7, 9},
{9, 7},
{8, 9},
{9, 8},
{0, 10}, // 90
{10, 0},
{1, 10},
{10, 1},
{2, 10},
{10, 2},
{3, 10},
{10, 3},
{4, 10},
{10, 4},
{5, 10}, // 100
{10, 5},
{6, 10},
{10, 6},
}

// clearDisplay turns off all the LEDs on the display by configuring all pins as input.
func (d *Device) clearDisplay() {
for i := machine.Pin(0); i < 11; i++ {
(d.pin + i).Configure(machine.PinConfig{Mode: machine.PinInput})
}
}

// turnLedOn turns on the LED at the given index (0-104).
func (d *Device) turnLedOn(idx int) {
if idx < 0 || idx >= len(pinMapping) {
return
}

pin0 := d.pin + pinMapping[idx][0]
pin1 := d.pin + pinMapping[idx][1]

pin0.Configure(machine.PinConfig{Mode: machine.PinOutput})
pin1.Configure(machine.PinConfig{Mode: machine.PinOutput})
pin0.High()
pin1.Low()
}